<?php
namespace App\Http\Middleware\TokenMiddleware;

use App\Exception\TokenException;
use App\Helper\JwtHelper;
use App\Http\Lib\Service;
use App\Http\Lib\ServiceInfo;
use Lcobucci\JWT\Parser;
use Lcobucci\JWT\Token;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Bean\BeanFactory;
use Swoft\Console\Router\Module;
use Swoft\Http\Message\ContentType;
use Swoft\Http\Message\Request;
use Swoft\Http\Message\Response;
use Swoft\Http\Server\Contract\MiddlewareInterface;
use Swoft\Redis\Redis;

/**
 * token 过期校验
 * Class ExpiredToken
 * @package App\Http\Middleware\TokenMiddleware
 * @Bean(name="ExpiredToken")
 */
class ExpiredToken implements MiddlewareInterface
{

    /**
     * @param ServerRequestInterface $request
     * @param RequestHandlerInterface $handler
     * @return ResponseInterface
     * @throws TokenException
     */
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        // 如果开启了JWT 校验则 进行 JWT token过期校验
        if (!constant(Module::DEFINE_DISABLE_JWT_MODULE)) {
            // 获取上级中间件中传递的服务信息
            /** @var Service $service */
            $service = $request->getAttribute('service');
            /** @var ServiceInfo $serviceInfo */
            $serviceInfo = $request->getAttribute('serviceInfo');
            // 判断 请求的 action 是否需要校验 这里使用公共函数判断
            if(need_valid($serviceInfo->getServiceName(), $serviceInfo->getServer(), $serviceInfo->getAction())){
                try {
                    // 获取用户参数
                    $requestData = $request->input();
                    // 首先校验 token 参数格式
                    validate($requestData, "TokenValidator", ['token']);
                    /** @var JwtHelper $JwtHelper */
                    $JwtHelper = BeanFactory::getBean('JwtHelper');
                    $token = (new Parser())->parse((string)$requestData['token']);
                } catch (\Exception $e) {
                    throw new TokenException("非法 Token", 460);
                }

                // token 验证成功  看看是否过期
                $uid = $token->getClaim('uid');
                if (Redis::ttl($JwtHelper->getRedisKey($uid)) > 0) {
                    //这里添加自动延期逻辑
                    if(Redis::ttl($JwtHelper->getRedisKey($uid)) < 500){
                        $JwtHelper->delayExpired($uid, (string)$requestData['token']);
                    }
                    return $handler->handle($request);
                }
                // token 已经过期了
                throw new TokenException("Token 已过期", 460);
            }
        }

        // 直接 下一步
        return $handler->handle($request);
    }
}
